package com.atguigu.gmall.product.service.impl;

import com.atguigu.gmall.common.cache.GmallCache;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.model.product.*;
import com.atguigu.gmall.mq.constants.MqConst;
import com.atguigu.gmall.mq.service.RabbitService;
import com.atguigu.gmall.product.mapper.*;
import com.atguigu.gmall.product.service.ManageService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors;

import static org.redisson.api.condition.Conditions.eq;

/**
 * @author Administrator
 * @create 2020-05-14 9:28
 */
@Service
@SuppressWarnings("all")
public class ManageServiceImpl implements ManageService {

    @Autowired
    private BaseCategory1Mapper baseCategory1Mapper;
    @Autowired
    private BaseCategory2Mapper baseCategory2Mapper;
    @Autowired
    private BaseCategory3Mapper baseCategory3Mapper;
    @Autowired
    private BaseAttrInfoMapper baseAttrInfoMapper;
    @Autowired
    private BaseAttrValueMapper baseAttrValueMapper;
    @Autowired
    private SpuInfoMapper spuInfoMapper;

    @Override
    public List<BaseCategory1> getCategory1() {
        return  baseCategory1Mapper.selectList(null);
    }

    @Override
    public List<BaseCategory2> getCategory2(long category1Id) {
        QueryWrapper<BaseCategory2> wrapper = new QueryWrapper<>();
        wrapper.eq("category1_id",category1Id);
        List<BaseCategory2> category2List = baseCategory2Mapper.selectList(wrapper);
        return category2List;
    }

    @Override
    public List<BaseCategory3> getCategory3(long category2Id) {
        QueryWrapper<BaseCategory3> wrapper = new QueryWrapper<>();
        wrapper.eq("category2_id",category2Id);
        List<BaseCategory3> category2List = baseCategory3Mapper.selectList(wrapper);
        return category2List;
    }

    @Override
    public List<BaseAttrInfo> attrInfoList(Long category1Id, Long category2Id, Long category3Id) {
        List<BaseAttrInfo> baseAttrInfos = baseAttrInfoMapper.attrInfoList(category1Id, category2Id, category3Id);
        return baseAttrInfos;
    }

    @Override
    public void saveAttrInfo(BaseAttrInfo baseAttrInfo) {
        //1.向base_attr_info表中添加数据
        baseAttrInfoMapper.insert(baseAttrInfo);
        //2.向base_attr_value表中添加数据
        List<BaseAttrValue> attrValueList = baseAttrInfo.getAttrValueList();//获取对象中的值
        attrValueList.forEach(attrValue -> {
            //添加外键
            attrValue.setAttrId(baseAttrInfo.getId());
            baseAttrValueMapper.insert(attrValue);
        });
    }

    @Override
    public IPage<SpuInfo> spuPage(Integer page, Integer limit, Long category3Id) {
        Page<SpuInfo> page1 = new Page<>(page, limit);
        QueryWrapper<SpuInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("category3_id",category3Id);
        return spuInfoMapper.selectPage(page1,wrapper);
    }

    @Autowired
    private BaseTrademarkMapper baseTrademarkMapper;
    @Autowired
    private BaseSaleAttrMapper baseSaleAttrMapper;
    @Autowired
    private SpuImageMapper spuImageMapper;
    @Autowired
    private SpuSaleAttrMapper spuSaleAttrMapper;
    @Autowired
    private SpuSaleAttrValueMapper spuSaleAttrValueMapper;
    @Autowired
    private SkuInfoMapper skuInfoMapper;
    @Autowired
    private SkuImageMapper skuImageMapper;
    @Autowired
    private SkuAttrValueMapper skuAttrValueMapper;
    @Autowired
    private SkuSaleAttrValueMapper skuSaleAttrValueMapper;

    @Override
    public List<BaseTrademark> getTrademarkList() {
        List<BaseTrademark> baseTrademarkList = baseTrademarkMapper.selectList(null);
        return baseTrademarkList;
    }

    @Override
    public List<BaseSaleAttr> baseSaleAttrList() {
        List<BaseSaleAttr> baseSaleAttrList = baseSaleAttrMapper.selectList(null);
        return baseSaleAttrList;
    }

    @Override //保存SPU
    public void saveSpuInfo(SpuInfo spuInfo) {
        //1.商品表
        spuInfoMapper.insert(spuInfo);
        //2.图片表
        List<SpuImage> spuImageList = spuInfo.getSpuImageList();
        spuImageList.forEach(spuImage -> {
            //外键
            spuImage.setSpuId(spuInfo.getId());
            spuImageMapper.insert(spuImage);
        });

        //3.商品销售属性表 spu_sale_attr
        List<SpuSaleAttr> spuSaleAttrList = spuInfo.getSpuSaleAttrList();
        spuSaleAttrList.forEach(spuSaleAttr -> {
            //外键
            spuSaleAttr.setSpuId(spuInfo.getId());
            spuSaleAttrMapper.insert(spuSaleAttr);
            //一个销售属性对应多个销售属性值
            List<SpuSaleAttrValue> spuSaleAttrValueList = spuSaleAttr.getSpuSaleAttrValueList();
            spuSaleAttrValueList.forEach(spuSaleAttrValue -> {
                //外键
                spuSaleAttrValue.setSpuId(spuInfo.getId());
                spuSaleAttrValue.setSaleAttrName(spuSaleAttr.getSaleAttrName());
                spuSaleAttrValueMapper.insert(spuSaleAttrValue);

            });
        });
    }

    @Override
    public List<SpuImage> spuImageList(Long spuId) {
        QueryWrapper<SpuImage> wrapper = new QueryWrapper<>();
        wrapper.eq("spu_id",spuId);
        List<SpuImage> imageList = spuImageMapper.selectList(wrapper);
        return imageList;
    }

    //根据SpuId查询销售属性及属性值
    @Override
    public List<SpuSaleAttr> spuSaleAttrList(Long spuId) {
        //手写Sql语句
        return spuSaleAttrMapper.spuSaleAttrList(spuId);
    }

    @Override
    public void saveSpuInfo(SkuInfo skuInfo) {
        //1.库存表
        skuInfoMapper.insert(skuInfo);
        //2.库存图片表
        List<SkuImage> skuImageList = skuInfo.getSkuImageList();
        skuImageList.forEach(skuImage -> {
            //外键：
            skuImage.setSkuId(skuInfo.getId());
            skuImageMapper.insert(skuImage);
        });
        //3.库存与平台属性关联表
        List<SkuAttrValue> skuAttrValueList = skuInfo.getSkuAttrValueList();
        skuAttrValueList.forEach(skuAttrValue -> {
            //外键
            skuAttrValue.setSkuId(skuInfo.getId());
            skuAttrValueMapper.insert(skuAttrValue);
        });
        //4.库存与销售属性值关联表
        List<SkuSaleAttrValue> skuSaleAttrValueList = skuInfo.getSkuSaleAttrValueList();
        skuSaleAttrValueList.forEach(skuSaleAttrValue -> {
            skuSaleAttrValue.setSpuId(skuInfo.getId());
            skuSaleAttrValueMapper.insert(skuSaleAttrValue);
        });
    }

    @Override
    public IPage<SkuInfo> getSkuList(Integer page, Integer limit) {
       return skuInfoMapper.selectPage(new Page<>(page, limit), null);
    }

    //上下架*********************************************************************************
    @Autowired
    private RabbitService rabbitService;
    @Override
    public void onSale(Long skuId) {
        SkuInfo skuInfo = new SkuInfo();
        skuInfo.setId(skuId);
        skuInfo.setIsSale(1);
        skuInfoMapper.updateById(skuInfo);
        //发消息给搜索微服务；添加索引  String exchange,String routingKey,Object msg
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_GOODS,MqConst.ROUTING_GOODS_UPPER,skuId);
    }

    @Override
    public void cancelSale(Long skuId) {
        SkuInfo skuInfo = new SkuInfo();
        skuInfo.setId(skuId);
        skuInfo.setIsSale(0);
        skuInfoMapper.updateById(skuInfo);
        //发消息给搜索微服务；删除索引
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_GOODS,MqConst.ROUTING_GOODS_LOWER,skuId);
    }

    //远程调用业务处理
    @Autowired
    private BaseCategoryViewMapper baseCategoryViewMapper;

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    //***************************************AOP编程***********************************************
    //根据skuId查询
    @Override
    @GmallCache(prefix = "getSkuInfoAop")
    public SkuInfo getSkuInfoAop(Long skuId) {
        return skuInfoMapper.selectById(skuId);
    }

    //根据三级分类ID查询一二三级分类
    @Override
    @GmallCache(prefix = "getCategoryViewAop")
    public BaseCategoryView getCategoryViewAop(Long category3Id) {
        return baseCategoryViewMapper.selectById(category3Id);
    }
    //根据spuId 查询map 集合属性
    @Override
    public Map getSkuValueIdsMapAop(Long spuId) {
        List<Map> skuValueIdsMap = skuSaleAttrValueMapper.getSkuValueIdsMap(spuId);
        return null;
    }

//****************************************************************************************
    //redis分布式锁的实现
    @Override
    public SkuInfo getSkuInfoRedisson(Long skuId) {
        String cacheKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKUKEY_SUFFIX;
        String lockKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKULOCK_SUFFIX;
        //1:优先从缓存中获取数据
        SkuInfo skuInfo = (SkuInfo) redisTemplate.opsForValue().get(cacheKey);
        if(skuId != null){
            return skuInfo;
        }else {
            //2.获取上锁的对象
            RLock lock = redissonClient.getLock(lockKey);//上锁 底层是随机的
            //lock.lock();//上锁，是可重入锁，如果锁在别人那儿，会一直等，处于阻塞状态
           // lock.lock(2,TimeUnit.SECONDS);//代码执行阻塞，锁到期，此时需设置看门狗，发现未手动解锁指定延迟时间

            //尝试锁：1.必须拿到锁   2.尝试性拿到锁
            try {
                //尝试锁  尝试的时间(未拿到返回false)  过期时间   时间单位
                boolean res = lock.tryLock(1, 3, TimeUnit.SECONDS);
                if(res){ //是第一个拿到锁的
                    //3.查询DB
                    skuInfo =  skuInfoMapper.selectById(skuId);
                    if(null == skuInfo){
                        skuInfo = new SkuInfo();
                        //3.2： 将空结果保存在缓存中5分分钟
                        redisTemplate.opsForValue().set(cacheKey, skuInfo, 5, TimeUnit.MINUTES);
                    }else {
                        //3.3查询图片信息 保存缓存中24小时
                        List<SkuImage> skuImages = skuImageMapper.selectList(new QueryWrapper<SkuImage>().eq("sku_id",skuId));
                        skuInfo.setSkuImageList(skuImages);
                        //4.解决缓存雪崩问题  添加一个随机数
                        Random random = new Random();
                        int i = random.nextInt(3000);
                        redisTemplate.opsForValue().set(cacheKey, skuInfo, RedisConst.SKUKEY_TIMEOUT+i, TimeUnit.SECONDS);
                    }
                    return skuInfo;
                }else {
                    //不是第一个拿到锁的 获取缓存中的就行
                    Thread.sleep(1000);
                    return (SkuInfo) redisTemplate.opsForValue().get(cacheKey);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                //5.手动释放锁
                lock.unlock();//封装了LUA脚本，具有原子性，防误删
            }
        }
        return skuInfo;
    }
    //根据三级分类ID查询一二三级分类
    @Override
    public BaseCategoryView getCategoryViewRedisson(Long category3Id) {
        //1.设置保存数据的key  和  锁的key   sku:+skuId+:info
        String cacheKey = RedisConst.SKUKEY_PREFIX+category3Id+RedisConst.SKUKEY_SUFFIX;
        String lockKey = RedisConst.SKUKEY_PREFIX + category3Id + RedisConst.SKULOCK_SUFFIX;
        BaseCategoryView bcView = (BaseCategoryView)redisTemplate.opsForValue().get(cacheKey);
        if(null !=bcView){
            return bcView;
        }else {
            //2.去数据库查 拿到锁对象
            RLock lock = redissonClient.getLock(lockKey);
            try {
                //上锁 尝试拿到锁
                boolean flag = lock.tryLock(1, 3, TimeUnit.SECONDS);
                if(flag){
                    //3.去数据库查
                    bcView = baseCategoryViewMapper.selectById(category3Id);
                    if(null ==bcView){
                        redisTemplate.opsForValue().set(cacheKey,new BaseCategoryView(),5,TimeUnit.MINUTES);
                    }else {
                        //为解决雪崩 添加一个随机数
                        Random random = new Random();
                        int i = random.nextInt(3000);//3000内的随机数
                        redisTemplate.opsForValue().set(cacheKey,bcView,RedisConst.SKUKEY_TIMEOUT+i,TimeUnit.SECONDS);
                    }
                }else {
                    //不是第一个拿到锁的 等着去查缓存
                    Thread.sleep(1000);
                    return (BaseCategoryView)redisTemplate.opsForValue().get(cacheKey);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }

        }
        return bcView;
    }
    //根据spuId，skuId 查询销售属性集合
    @Override
    public List<SpuSaleAttr> getSpuSaleAttrListCheckBySkuRedisson(Long skuId, Long spuId) {
        String cacheKey = RedisConst.SKUKEY_PREFIX + skuId + spuId + RedisConst.SKUKEY_SUFFIX;
        String lockKey = RedisConst.SKUKEY_PREFIX + skuId + spuId + RedisConst.SKULOCK_SUFFIX;
        List<SpuSaleAttr> spuSaleAttrList = (List<SpuSaleAttr>)redisTemplate.opsForValue().get(cacheKey);
        if (null != spuSaleAttrList){
            return spuSaleAttrList;
        }else {
            RLock lock = redissonClient.getLock(lockKey); //拿到锁对象
            try {
                boolean lock1 = lock.tryLock(1, 3, TimeUnit.SECONDS);//上锁
                if(lock1){
                    //去数据库查
                    spuSaleAttrList =  spuSaleAttrMapper.getSpuSaleAttrListCheckBySku(skuId,spuId);
                    //为防止穿透，将null值也存放到缓存中  存活时间一般5分钟
                    if (null == spuSaleAttrList){
                        redisTemplate.opsForValue().set(cacheKey,new SpuSaleAttr(),5,TimeUnit.MINUTES);
                    }
                    //为解决雪崩 添加一个随机数
                    Random random = new Random();
                    int i = random.nextInt(3000);//3000内的随机数
                    redisTemplate.opsForValue().set(cacheKey,spuSaleAttrList,RedisConst.SKUKEY_TIMEOUT+i,TimeUnit.SECONDS);
                }else {
                    //不是第一个拿到锁的 等着去查缓存
                    return (List<SpuSaleAttr>)redisTemplate.opsForValue().get(cacheKey);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
        return spuSaleAttrList;
    }
    //根据spuId 查询map 集合属性
    @Override
    public Map getSkuValueIdsMapRedisson(Long spuId) {
        String cacheKey = RedisConst.SKUKEY_PREFIX + spuId + RedisConst.SKUKEY_SUFFIX;
        String lockKey = RedisConst.SKUKEY_PREFIX + spuId + RedisConst.SKULOCK_SUFFIX;
        Map map =  (Map)redisTemplate.opsForValue().get(cacheKey);
        if(null != map){
            return map;
        }else {
            RLock lock = redissonClient.getLock(lockKey);
            try {
                boolean tryLock = lock.tryLock(1, 3, TimeUnit.SECONDS);
                if(tryLock){
                    //去数据库查
                    Map map1 = new HashMap();
                    List<Map> skuValueIdsMap = skuSaleAttrValueMapper.getSkuValueIdsMap(spuId);
                    if(null == skuValueIdsMap){
                        redisTemplate.opsForValue().set(cacheKey,map1,5,TimeUnit.MINUTES);
                    }
                    skuValueIdsMap.forEach(skuValue-> {
                        map1.put(skuValue.get("value_ids"),skuValue.get("sku_id"));
                    });
                    //为防止雪崩 加一个随机数
                    Random random = new Random();
                    int i = random.nextInt(3000);
                    redisTemplate.opsForValue().set(cacheKey,map1,RedisConst.SKUKEY_TIMEOUT+i,TimeUnit.SECONDS);
                }else {
                    Thread.sleep(1000);
                    return (Map)redisTemplate.opsForValue().get(cacheKey);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    return map;
    }
    //========================================================================================================
    @Override
    public SkuInfo getSkuInfo(Long skuId) {
        //1.设置保存数据的key  和  锁的key   sku:+skuId+:info
        String cacheKey = RedisConst.SKUKEY_PREFIX+skuId+RedisConst.SKUKEY_SUFFIX;
        String lockKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKULOCK_SUFFIX;
        //2.去缓存中查找，看有没有
        SkuInfo skuInfo = (SkuInfo)redisTemplate.opsForValue().get(cacheKey);
        if(null != skuInfo){
            return skuInfo;
        }else {
            //6.为防止误删，使用一个随机字符串
            String uuid  = UUID.randomUUID().toString();
            //5.决绝缓存击穿问题 刚好在缓存结束时同一个请求高并发 上锁  锁的持有时间为1秒，1秒有释放锁
            //Boolean isLock = redisTemplate.opsForValue().setIfAbsent(lockKey,"haha", 1, TimeUnit.SECONDS);
            Boolean isLock = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid, 1, TimeUnit.SECONDS);
            if (isLock){
                //3.缓存中没有 去DB中查找
                //3.1解决缓存穿透问题 缓存中无法获取，数据库中没有这个数据，将结果null值也保存在缓存中
                skuInfo =  skuInfoMapper.selectById(skuId);
                if(null == skuInfo){
                    skuInfo = new SkuInfo();
                    //3.2： 将空结果保存在缓存中5分分钟
                    redisTemplate.opsForValue().set(cacheKey, skuInfo, 5, TimeUnit.MINUTES);
                }else {
                    //3.3查询图片信息 保存缓存中24小时
                    List<SkuImage> skuImages = skuImageMapper.selectList(new QueryWrapper<SkuImage>().eq("sku_id",skuId));
                    skuInfo.setSkuImageList(skuImages);
                    //4.解决缓存雪崩问题  添加一个随机数
                    Random random = new Random();
                    int i = random.nextInt(3000);
                    redisTemplate.opsForValue().set(cacheKey, skuInfo, RedisConst.SKUKEY_TIMEOUT+i, TimeUnit.SECONDS);
                }
             //锁用完后要释放锁
                //redisTemplate.delete(lockKey);当当前线程卡顿，别人拿到了锁执行代码是，当前线程执行到删除锁时，会把别人的锁删除
               //防止误删
                String u = (String) redisTemplate.opsForValue().get(lockKey);
                /*if(!StringUtils.isEmpty(u) && uuid.equals(u)){//是自己的锁
                       redisTemplate.delete(lockKey);  //但这个这个操作分3步，不具有原子性 使用LUA脚本
                }*/
                //使用LUA脚本具有原子性操作  script是脚本
                String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return tostring(redis.call('del',KEYS[1])) else return 0 end";
                this.redisTemplate.execute(new DefaultRedisScript<>(script), Collections.singletonList(lockKey), uuid);
            }else { //别人上锁，我们等待一会后去缓存查
                try {
                    Thread.sleep(1000);
                    //重新加载当前方法
                    return getSkuInfo(skuId);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    }
        return skuInfo;
       /* SkuInfo skuInfo = skuInfoMapper.selectById(skuId);
        //查询图片
        List<SkuImage> skuImages = skuImageMapper.selectList(new QueryWrapper<SkuImage>().eq("sku_id",skuId));
        skuInfo.setSkuImageList(skuImages);*/
        //return skuInfo;
    }

    //根据三级分类ID查询一二三级分类
    @Override
    public BaseCategoryView getCategoryView(Long category3Id) {
        //1.设置保存数据的key  和  锁的key   sku:+skuId+:info
        String cacheKey = RedisConst.SKUKEY_PREFIX+category3Id+RedisConst.SKUKEY_SUFFIX;
        String lockKey = RedisConst.SKUKEY_PREFIX + category3Id + RedisConst.SKULOCK_SUFFIX;
        BaseCategoryView bcv = (BaseCategoryView)redisTemplate.opsForValue().get(cacheKey);
        if(null != bcv){
            return bcv;
        }else {
            //上锁
            String uuid = UUID.randomUUID().toString();
            Boolean isLock = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid, 1, TimeUnit.SECONDS);
            if(isLock){
               bcv = baseCategoryViewMapper.selectById(category3Id);
               if(null ==bcv){
                   redisTemplate.opsForValue().set(cacheKey,new BaseCategoryView(),5,TimeUnit.MINUTES);
               }else {
                   //为解决雪崩 添加一个随机数
                   Random random = new Random();
                   int i = random.nextInt(3000);//3000内的随机数
                   redisTemplate.opsForValue().set(cacheKey,bcv,RedisConst.SKUKEY_TIMEOUT+i,TimeUnit.SECONDS);
               }
                //最后释放锁
                String lock = (String)redisTemplate.opsForValue().get(lockKey);
                //使用LUA脚本具有原子性操作  script是脚本
                String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return tostring(redis.call('del',KEYS[1])) else return 0 end";
                this.redisTemplate.execute(new DefaultRedisScript<>(script), Collections.singletonList(lock), uuid);
            }else {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return getCategoryView(category3Id);//回调
            }
        }
        return bcv;
    }

    //根据skuId查询库存表中的价格
    @Override
    public BigDecimal getSkuPrice(Long skuId) {
        SkuInfo skuInfo = skuInfoMapper.selectById(skuId);
        if(null != skuInfo){
            return skuInfo.getPrice();
        }
        return null;
    }

    //根据spuId，skuId 查询销售属性集合
    @Override
    public List<SpuSaleAttr> getSpuSaleAttrListCheckBySku(Long skuId, Long spuId) {
        String cacheKey = RedisConst.SKUKEY_PREFIX + skuId + spuId + RedisConst.SKUKEY_SUFFIX;
        String lockKey = RedisConst.SKUKEY_PREFIX + skuId + spuId + RedisConst.SKULOCK_SUFFIX;
        List<SpuSaleAttr> spuSaleAttrList = (List<SpuSaleAttr>)redisTemplate.opsForValue().get(cacheKey);
        if (null != spuSaleAttrList){
            return spuSaleAttrList;
        }else {
            //1.防止穿透加锁  防止误删使用随机数
            String uuid = UUID.randomUUID().toString();
            Boolean isLock = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid, 1, TimeUnit.SECONDS);
            if(isLock){
                //去数据库查
                spuSaleAttrList =  spuSaleAttrMapper.getSpuSaleAttrListCheckBySku(skuId,spuId);
                //为防止穿透，将null值也存放到缓存中  存活时间一般5分钟
                if (null == spuSaleAttrList){
                   redisTemplate.opsForValue().set(cacheKey,new SpuSaleAttr(),5,TimeUnit.MINUTES);
                }
                //为解决雪崩 添加一个随机数
                Random random = new Random();
                int i = random.nextInt(3000);//3000内的随机数
                redisTemplate.opsForValue().set(cacheKey,spuSaleAttrList,RedisConst.SKUKEY_TIMEOUT+i,TimeUnit.SECONDS);
                //释放锁
                String lock = (String)redisTemplate.opsForValue().get(lockKey);
                //使用LUA脚本具有原子性操作  script是脚本
                String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return tostring(redis.call('del',KEYS[1])) else return 0 end";
                this.redisTemplate.execute(new DefaultRedisScript<>(script), Collections.singletonList(lock), uuid);
                return spuSaleAttrList;
            }else {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return getSpuSaleAttrListCheckBySku(skuId, spuId);
            }
        }
    }

    //根据spuId 查询map 集合属性
    @Override
    public Map getSkuValueIdsMap(Long spuId) {
        String cacheKey = RedisConst.SKUKEY_PREFIX + spuId + RedisConst.SKUKEY_SUFFIX;
        String lockKey = RedisConst.SKUKEY_PREFIX + spuId + RedisConst.SKULOCK_SUFFIX;
        Map map =  (Map)redisTemplate.opsForValue().get(cacheKey);
        if(null != map){
            return map;
        }else {
            //防止缓存击穿==上锁 防止误删使用随机字符串
            String uuid = UUID.randomUUID().toString();
            Boolean isLock = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid, 1, TimeUnit.SECONDS);
            if(isLock){
                //去数据库查
                Map map1 = new HashMap();
                List<Map> skuValueIdsMap = skuSaleAttrValueMapper.getSkuValueIdsMap(spuId);
                if(null == skuValueIdsMap){
                    redisTemplate.opsForValue().set(cacheKey,map1,5,TimeUnit.MINUTES);
                }
                skuValueIdsMap.forEach(skuValue-> {
                    map1.put(skuValue.get("value_ids"),skuValue.get("sku_id"));
                });
                //为防止雪崩 加一个随机数
                Random random = new Random();
                int i = random.nextInt(3000);
                redisTemplate.opsForValue().set(cacheKey,map1,RedisConst.SKUKEY_TIMEOUT+i,TimeUnit.SECONDS);
                //释放锁
                String lock = (String)redisTemplate.opsForValue().get(lockKey);
                String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return tostring(redis.call('del',KEYS[1])) else return 0 end";
                this.redisTemplate.execute(new DefaultRedisScript<>(script), Collections.singletonList(lock), uuid);
                return map;
            }else {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return getSkuValueIdsMap(spuId);
            }
        }
       /* //Map map = new HashMap();
        List<Map> skuValueIdsMap = skuSaleAttrValueMapper.getSkuValueIdsMap(spuId);
        skuValueIdsMap.forEach(skuValue-> {
            map.put(skuValue.get("value_ids"),skuValue.get("sku_id"));
        });
        return map;*/
    }


    @Override
    public IPage<BaseTrademark> baseTrademark(Integer page, Integer limit) {
        return baseTrademarkMapper.selectPage(new Page<>(page, limit), null);
    }

    @Override
    public List<BaseAttrValue> getAttrValueList(Long categoryId) {
        return baseAttrValueMapper.selectList(new QueryWrapper<BaseAttrValue>().eq("attr_id", categoryId));
    }

    //查询一二三级分类
    @Override
    public List<Map> getBaseCategoryMap(){
        //1.准备最终返回的List<Map>集合
        List<Map> result = new ArrayList<>();
        //2.从DB中查询数据
        List<BaseCategoryView> baseCategoryViews = baseCategoryViewMapper.selectList(null);
        //3.循环获得一个分类
        Map<Long, List<BaseCategoryView>> category1List
                = baseCategoryViews.stream().collect(Collectors.groupingBy(BaseCategoryView::getCategory1Id));
       int index = 1;
       List<Map> category1ListMap= new ArrayList<>();
       for(Map.Entry<Long, List<BaseCategoryView>> category1IdEntry : category1List.entrySet()){
           Map map1 = new HashMap();
           //添加index
           map1.put("index",index++);
           //一级分类ID
           map1.put("categoryId",category1IdEntry.getKey());
           //一个分类的categoryName
           map1.put("categoryName",category1IdEntry.getValue().get(0).getCategory1Name());
           //2.获取二级节点的集合
           Map<Long, List<BaseCategoryView>> category2List =
                   category1IdEntry.getValue().stream().collect(Collectors.groupingBy(BaseCategoryView::getCategory2Id));
           List<Map> category2ListMap= new ArrayList<>();
           for (Map.Entry<Long, List<BaseCategoryView>> category2IdEntry : category2List.entrySet()){
               Map map2 = new HashMap();
               //添加二级分类Id
               map2.put("categoryId",category2IdEntry.getKey());
               //名称
               map2.put("categoryName",category2IdEntry.getValue().get(0).getCategory2Name());
               //3.获取3级分类的集合
               Map<Long, List<BaseCategoryView>> category3List =
                       category2IdEntry.getValue().stream().collect(Collectors.groupingBy(BaseCategoryView::getCategory3Id));
               List<Map> category3ListMap= new ArrayList<>();
               for (Map.Entry<Long, List<BaseCategoryView>> category3IdEntry : category3List.entrySet()){
                   Map map3 = new HashMap();
                   map3.put("categoryId",category3IdEntry.getKey());
                   map3.put("categoryName",category3IdEntry.getValue().get(0).getCategory3Name());
                   category3ListMap.add(map3);
               }
              map2.put("categoryChild",category3ListMap);
               category2ListMap.add(map2);
           }
           map1.put("categoryChild",category2ListMap);
           result.add(map1);
       }
            return result;
    }

    //根据品牌id查询品牌信息
    @Override
    public BaseTrademark getBaseTrademark(Long tmId) {
         return baseTrademarkMapper.selectById(tmId);
    }

    //根据SkuId查询平台属性id/名称 平台属性值的名称
    @Override
    public List<SkuAttrValue> getAttrList(Long skuId) {
        return skuAttrValueMapper.getAttrList(skuId);
    }
}
